
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package agen.optimalidad;

import java.util.logging.Level;
import java.util.logging.Logger;
import no.uib.cipr.matrix.DenseMatrix;
import no.uib.cipr.matrix.EVD;
import no.uib.cipr.matrix.NotConvergedException;
import Jampack.*;
import agen.util.Arreglos;
import java.util.Arrays;
import org.jblas.*;
//TODO: averiguar si las aptitudes (LamMax - m) puede ser negativa
/**
 * En esta clase se agrupan todos los metodos relacionados con el cálculo de
 * aptitudes.
 * Concepto de aptitud:
 *  El valor de (autovalorMax - orden) de la matriz debe ser lo más sercano a
 *  cero
 *
 * @author juan
 */
public class Aptitud {

    //static double[] autovalores;
    static double[] aptitudes;
    static int raicesReales = 0;
    static int individuosvistos = 0;
    static double promedioAptitudes;
    static double[] historialPromedioAptitudes = new double[100];

    public static double getPromedioAptitudes(int generacion) {
        return historialPromedioAptitudes[generacion];
    }
// <editor-fold defaultstate="collapsed" desc="MTJ">
    /**
     * Para cada individuo, completa la matriz de prioridades con los elementos
     * del mismo y usa la librería MTJ (Matrix Toolkit for Java) para el calculo
     * de los autovalores.
     * @param Individuos    Es la población implentada con un array byte[]
     * @param prioridades   Los posibles valores de prioridades los valores en
     *                      Individuos son indices para double[] prioridaes
     * @param ubicaciones   las posisiones, fila y columna, donde se ubica cada
     *                      elto de in individuo en la matriz multi criterio.
     * @param matriz        La matriz multi criterio.
     * @param armarArrayOrdenado    Si es distinto de null se ordena el array de
     *                              aptitudes y se almacena en cada elemento
     *                              la posición que ocupaba originalmente
     *                              una aptitud antes de ser ordenada.
     * @return              double[] aptitudes.
     
     */
    public static double[] calcularAptitudMTJ(byte[] Individuos, double[] prioridades,
            int[][] ubicaciones, double[][] matriz,int[] armarArrayOrdenado) {
        int orden = matriz.length;
        DenseMatrix matrix = new DenseMatrix(matriz);
        EVD evd = new EVD(matriz.length);//EVD.factorize(matrix);
        aptitudes = new double[Individuos.length / ubicaciones.length];
        //ubicaciones.length coincide con la cantodad de elementos del individuo
        int aptitudIndex = 0;
        try {
            for (int i = Individuos.length - 1; i >= 0; i -= ubicaciones.length) {
                for (int j = ubicaciones.length - 1; j >= 0; j--) {
                    /*Importante tener en cuenta que la ubicacion debe cargarse al
                     * revez*/
                    matrix.set(ubicaciones[j][0], ubicaciones[j][1],
                            prioridades[Individuos[i - j]]);
                    matrix.set(ubicaciones[j][1], ubicaciones[j][0],
                            1 / prioridades[Individuos[i - j]]);

                }
                aptitudes[aptitudIndex++] = autovalorMaxMTJ(matrix, evd)
                        - orden;
            }
            /*Puede resultar util ordenar los indiiduos por aptitud*/
            if (armarArrayOrdenado != null) {
                if (armarArrayOrdenado.length != aptitudes.length) {
                    armarArrayOrdenado = new int[aptitudes.length];
                }
                
                Arreglos.sort1(aptitudes, armarArrayOrdenado, 0, aptitudes.length);
            }
        } catch (NotConvergedException ex) {
            System.out.println("***Aptitud.calcularAptitud(): ");
            Logger.getLogger(Aptitud.class.getName()).log(Level.SEVERE, null, ex);
        }
        return aptitudes;
    }

    /**
     * Calcula los autovalores de una matriz usando MTJ e itera por ellos en
     * busca del máximo
     * @param matrix
     * @return
     * @throws no.uib.cipr.matrix.NotConvergedException
     */
    private static double autovalorMaxMTJ(DenseMatrix matrix, EVD evd)
            throws NotConvergedException {
        /*eigen value decomposition (evd)*/
        evd = evd.factor(matrix);
        double[] autovalores = evd.getRealEigenvalues();
        double maxD = Double.NEGATIVE_INFINITY;
        for (double d : autovalores) {
            if (maxD < d) {
                maxD = d;
            }
        }
        return maxD;
    }
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="Jampack">
    /**
     * Para cada individuo, completa la matriz de prioridades con los elementos
     * del mismo y usa la librería Jampack (Java Matrix Package) para el calculo
     * de los autovalores.
     * @param Individuos    Es la población implentada con un array byte[]
     * @param prioridades   Los posibles valores de prioridades los valores en
     *                      Individuos son indices para double[] prioridaes
     * @param ubicaciones   las posisiones, fila y columna, donde se ubica cada
     *                      elto de in individuo en la matriz multi criterio.
     * @param matriz        La matriz multi criterio.
     * @param armarArrayOrdenado    Si es distinto de null se ordena el array de
     *                              aptitudes y se almacena en cada elemento
     *                              la posición que ocupaba originalmente
     *                              una aptitud antes de ser ordenada.
     * @return              double[] aptitudes.
     */
    public static double[] calcularAptitudJampack(byte[] Individuos, double[] prioridades,
            int[][] ubicaciones, double[][] matriz, int[] armarArrayOrdenado) {
        int orden = matriz.length;
        Zmat matrix = new Zmat(matriz);
        aptitudes = new double[Individuos.length / ubicaciones.length];
        //ubicaciones.length coincide con la cantodad de elementos del individuo
        int aptitudIndex = 0;
        try {
            for (int i = Individuos.length - 1; i >= 0; i -= ubicaciones.length) {
                for (int j = ubicaciones.length - 1; j >= 0; j--) {
                    /*Importante tener en cuenta que la ubicacion debe cargarse al
                     * revez*/
                    matrix.put0(ubicaciones[j][0], ubicaciones[j][1],
                            new Z(prioridades[Individuos[i - j]]));
                    matrix.put0(ubicaciones[j][1], ubicaciones[j][0],
                            new Z(1 / prioridades[Individuos[i - j]]));
                }
                aptitudes[aptitudIndex++] = autovalorMaxJampack(matrix)
                        - orden;
            }
            /*Puede resultar util ordenar los indiiduos por aptitud*/
            if (armarArrayOrdenado != null) {
                if (armarArrayOrdenado.length != aptitudes.length) {
                    armarArrayOrdenado = new int[aptitudes.length];
                }
                /*Inicialmente cada elto debe tener su indice como valor*/
                for (int i = armarArrayOrdenado.length - 1; i >= 0; i--) {
                    armarArrayOrdenado[i] = i;
                }
                Arreglos.sort1(aptitudes, armarArrayOrdenado, 0, aptitudes.length);
            }
        } catch (JampackException ex) {
            Logger.getLogger(Aptitud.class.getName()).log(Level.SEVERE, null, ex);
        }
        return aptitudes;
    }
    /**
     * Calcula los autovalores de una matriz usando Jampack e itera por ellos en
     * busca del máximo
     * @param matrix
     * @return
     * @throws Jampack.JampackException
     */
    private static double autovalorMaxJampack(Zmat matrix) throws JampackException {
        Jampack.Eig autoValorVector = new Eig(matrix);
        double maxAutV = Double.NEGATIVE_INFINITY;
        Z autV;
        for (int i = autoValorVector.D.n - 1; i >= 0; i--) {
            autV = autoValorVector.D.get0(i);
            /*Si la parte compleja difiere de 0 no tomo en cuenta el autovalor
             * En realidad casi nunca difiere de 0 por eso la comparación con
             * 0.00000000001
             */
            if (maxAutV < autV.re && Math.abs(autV.im) < 0.00000000001) {
                maxAutV = autV.re;
            //System.out.println("hay raices reales: " + ++raicesReales);
            }
        }
        //System.out.println("individuo: " + ++individuosvistos);
        return maxAutV;
    }

    // </editor-fold>                          
    
// <editor-fold defaultstate="collapsed" desc="Jblas">

    /**
     * Para cada individuo, completa la matriz de prioridades con los elementos
     * del mismo y usa la librería Jblas para el calculo de los autovalores.
     *
     * @param Individuos    Es la población implentada con un array byte[]
     * @param prioridades   Los posibles valores de prioridades los valores en
     *                      Individuos son indices para double[] prioridaes
     * @param ubicaciones   las posisiones, fila y columna, donde se ubica cada
     *                      elto de in individuo en la matriz multi criterio.
     * @param matriz        La matriz multi criterio.
     * @param armarArrayOrdenado    Si es distinto de null se ordena el array de
     *                              aptitudes y se almacena en cada elemento
     *                              la posición que ocupaba originalmente
     *                              una aptitud antes de ser ordenada.
     * @return              double[] aptitudes.
     */
    public static double[] calcularAptitudJblas(byte[] poblacion, double[] prioridades,
            int[][] ubicaciones, DoubleMatrix matriz, int[] armarArrayOrdenado,
            int iteracion) {
        int orden = matriz.rows;
        aptitudes = new double[poblacion.length / ubicaciones.length];
        //ubicaciones.length coincide con la cantodad de elementos del individuo
        int aptitudIndex = 0;
        promedioAptitudes = 0;
            //for (int i = poblacion.length - 1; i >= 0; i -= ubicaciones.length) {
        for (int i = 0; i < poblacion.length ; i += ubicaciones.length) {
                for (int j = ubicaciones.length - 1; j >= 0; j--) {
                    /*Importante tener en cuenta que la ubicacion debe cargarse al
                     * revez*/
                    matriz.put(ubicaciones[j][0], ubicaciones[j][1],
                            prioridades[poblacion[i + j]]);
                    matriz.put(ubicaciones[j][1], ubicaciones[j][0],
                            1 / prioridades[poblacion[i + j]]);
                }

                aptitudes[aptitudIndex] = autovalorMaxJblas(matriz)
                        - orden;
                promedioAptitudes += aptitudes[aptitudIndex++];
//                if (aptitudes[aptitudIndex - 1] < 0) {
//                    System.out.println("La aptitud con indice: " + (aptitudIndex - 1)  + " es < 0 y es: " + aptitudes[aptitudIndex - 1]);
//                }
            }
			promedioAptitudes /= (double) aptitudes.length;
            if (historialPromedioAptitudes.length == iteracion ){
                historialPromedioAptitudes = Arrays.copyOf(historialPromedioAptitudes
                        , historialPromedioAptitudes.length + 100);
            }
            historialPromedioAptitudes[iteracion] = promedioAptitudes;
            /*Resulta util ordenar los individuos por aptitud*/
            if (armarArrayOrdenado != null) {
                Arreglos.sort1(aptitudes, armarArrayOrdenado, 0, aptitudes.length);
            }
        return aptitudes;
    }
    /**
     * Calcula los autovalores de una matriz usando Jblas e itera por ellos en
     * busca del máximo
     * @param matrix
     * @return
     * @throws Jampack.JampackException
     */
    private static double autovalorMaxJblas(DoubleMatrix matrix)  {
        /*
         * De leer el codigo, porque la documentacion apesta, veo que almacena
         * todo en un float[] donde indice 0: primer real indice 1 primer imaginario.
         * indice 0 y 1 : primer complejo. Aunque Array.length respeta el orden
         * de la matriz.
         */
        ComplexDoubleMatrix autoValores = Eigen.eigenvalues(matrix);
        double maxAutV = Float.NEGATIVE_INFINITY;
        int i = autoValores.length * 2;
        while (--i >= 0) {
            /*Pregunto por la parte imaginaria y decremento, luego por la real*/
            if (autoValores.data[i--] == 0 && maxAutV < autoValores.data[i]) {
                maxAutV = autoValores.data[i];
            }
        }
        return maxAutV;
    }
    // </editor-fold>                          
  
// <editor-fold defaultstate="collapsed" desc="bench Jblas">
    /**
     * metodo usado para probar la performance de la libreria jblas
     * @param orden
     * @return
     */
    public static long pruebaAutovalor(int orden) {

        long salida = System.currentTimeMillis();
        try {

            DoubleMatrix matrix = DoubleMatrix.rand(orden, orden);

            System.out.println("Matriz generada en: " + (System.currentTimeMillis() - salida));
            autovalorMaxJblas(matrix);
        } catch (Exception e) {
            System.out.println("hubo un error en Aptitud.pruebaAutovalor");

        }
        salida = System.currentTimeMillis() - salida;
        return salida;
    }
    // </editor-fold>
}